unit SDFrameDealItemViewer;

interface

uses
  Windows, Messages, Forms, BaseFrame, StdCtrls, Classes, Controls, ExtCtrls,
  Dialogs, Sysutils, VirtualTrees,
  define_dealitem, define_dealmarket,
  DealItemsTreeView;

type                  
  TfmeDealItemViewerData = record   
    DealItemTree: TDealItemTreeCtrl;
    OnDealItemChange: TOnDealItemEvent;
  end;
  
  TfmeDealItemViewer = class(TfmeBase)
    pnlLeftBottom: TPanel;
    btnClear: TButton;
    btnSaveDic: TButton;
    btnSaveTxt: TButton; 
    btnCheckEnd: TButton;
    btnOpen: TButton;
    btnSaveIni: TButton;
    btnRefresh: TButton;
    procedure btnClearClick(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
    procedure btnSaveDicClick(Sender: TObject);
    procedure btnSaveIniClick(Sender: TObject);
    procedure btnRefreshClick(Sender: TObject);
  protected
    { Private declarations }
    fDealItemViewerData: TfmeDealItemViewerData;
    procedure DealItemChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
    procedure BuildDealItemsTree();

    procedure SaveDealItemDBAsDic(AFileUrl: string);
    procedure SaveDealItemDBAsIni(AFileUrl: string);
  public
    { Public declarations }
    procedure Initialize; override;
    property OnDealItemChange: TOnDealItemEvent read fDealItemViewerData.OnDealItemChange write fDealItemViewerData.OnDealItemChange;
  end;

implementation

uses
  UtilsHttp,                  
  {$IFDEF LOG}
  UtilsLog,
  {$ENDIF}
  win.iobuffer,
  HTMLParserAll3,
  BaseStockFormApp,
  db_dealitem_loadIni,
  db_dealitem_load, db_dealitem_save;
               
{$IFNDEF RELEASE}  
const
  LOGTAG = 'SDFrameDealItemViewer.pas'; 
{$ENDIF}
    
{$R *.dfm}

procedure TfmeDealItemViewer.Initialize;
begin
  fDealItemViewerData.DealItemTree := TDealItemTreeCtrl.Create(Self);
  fDealItemViewerData.DealItemTree.InitializeDealItemsTree(fDealItemViewerData.DealItemTree.TreeView);

  fDealItemViewerData.DealItemTree.AddDealItemsTreeColumn_FirstDeal;
  //fFormSDConsoleData.DealItemTree.AddDealItemsTreeColumn_lastDeal;    
  fDealItemViewerData.DealItemTree.AddDealItemsTreeColumn_EndDeal;
                                                         
  TVirtualStringTree(fDealItemViewerData.DealItemTree.TreeView).TreeOptions.SelectionOptions :=
    TVirtualStringTree(fDealItemViewerData.DealItemTree.TreeView).TreeOptions.SelectionOptions + [toMultiSelect];
  TVirtualStringTree(fDealItemViewerData.DealItemTree.TreeView).OnChange := DealItemChange;
  BuildDealItemsTree;
  btnClear.OnClick := btnClearClick;
  btnOpen.OnClick := btnOpenClick;
  btnSaveDic.OnClick := btnSaveDicClick;
  btnSaveIni.OnClick := btnSaveIniClick;
  btnRefresh.OnClick := btnRefreshClick;
end;

procedure TfmeDealItemViewer.BuildDealItemsTree();
begin                                     
  {$IFDEF LOG}
  //LOG(LOGTAG, 'TfmeDealItemViewer.BuildDealItemsTree begin');
  {$ENDIF}
  fDealItemViewerData.DealItemTree.TreeView.Clear;
  fDealItemViewerData.DealItemTree.BuildDealItemsTreeNodes(TBaseStockApp(App).StockIndexDB);
  fDealItemViewerData.DealItemTree.BuildDealItemsTreeNodes(TBaseStockApp(App).StockItemDB);               
  {$IFDEF LOG}
  //LOG(LOGTAG, 'TfmeDealItemViewer.BuildDealItemsTree end');
  {$ENDIF}
end;

procedure TfmeDealItemViewer.DealItemChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
  tmpNode: PDealItemNode;
begin                         
  {$IFDEF LOG}
  //LOG(LOGTAG, 'TfmeDealItemViewer.DealItemChange begin');
  {$ENDIF}
  if nil <> Node then
  begin
    tmpNode := fDealItemViewerData.DealItemTree.TreeView.GetNodeData(Node);
    if nil <> tmpNode then
    begin
      //LoadDayDataTreeView(tmpNode);
      if Assigned(OnDealItemChange) then
      begin
        OnDealItemChange(tmpNode.DealItem);
      end;
    end;
  end;               
  {$IFDEF LOG}
  //LOG(LOGTAG, 'TfmeDealItemViewer.DealItemChange end');
  {$ENDIF}
end;

procedure TfmeDealItemViewer.btnOpenClick(Sender: TObject);
var
  tmpFileUrl: string;
  tmpOpenDlg: TOpenDialog;
  i: integer;
  tmpIsDone: Boolean;
begin
  tmpOpenDlg := TOpenDialog.Create(Self);
  try
    tmpOpenDlg.InitialDir := ExtractFilePath(ParamStr(0));
    tmpOpenDlg.DefaultExt := '.dic';
    tmpOpenDlg.Filter := 'dic file|*.dic|ini file|*.ini';
    tmpOpenDlg.Options := tmpOpenDlg.Options + [ofAllowMultiSelect];
    //tmpOpenDlg.OptionsEx := [];   
    if not tmpOpenDlg.Execute then
      exit;
    tmpIsDone := false;
    for i := 0 to tmpOpenDlg.Files.Count - 1 do
    begin
      tmpFileUrl := tmpOpenDlg.Files[i];
      if 0 < Pos('.ini', lowercase(tmpFileUrl)) then
      begin
        db_dealItem_LoadIni.LoadDBStockItemIniFromFile(App, TBaseStockApp(App).StockItemDB, tmpFileUrl);
        tmpIsDone := true;
      end;                        
      if 0 < Pos('.dic', lowercase(tmpFileUrl)) then
      begin
        db_dealItem_Load.LoadDBStockItemDicFromFile(App, TBaseStockApp(App).StockItemDB, tmpFileUrl);
        tmpIsDone := true;
      end;
    end;
    if tmpIsDone then
    begin
      BuildDealItemsTree;
    end;
  finally
    tmpOpenDlg.Free;
  end;
end;
                               
type                            
  PParseRecord = ^TParseRecord;
  TParseRecord = record        
    HeadParse: THttpHeadParseSession;
    HtmlDoc: PHtmlDocDomNode;
    IsInDiv: Integer;
    Stocks: TStringList;
  end;

function getNodeText(ANode: PHtmlDomNode): string;
var
  i: integer;   
  tmpNode: PHtmlDomNode;
begin
  Result := '';
  if nil = ANode then
    exit;
  Result := ANode.NodeValue;
  if nil <> ANode.ChildNodes then
  begin
    for i := 0 to ANode.ChildNodes.Length - 1 do
    begin
      tmpNode := ANode.ChildNodes.item(i);
      Result := result + tmpNode.NodeValue;
    end;
  end;
end;
                      
function getAttribValue(ANode: PHtmlDomNode; AttribName: string): string;
var
  i: integer;   
  tmpNode: PHtmlDomNode;
begin
  Result := '';
  if nil = ANode then
    exit;
  if '' = AttribName then
    exit;
  for i := 0 to ANode.attributes.length - 1 do
  begin
    tmpNode := ANode.attributes.item(i);
    if SameText(AttribName, tmpNode.nodeName) then
    begin
      Result := getNodeText(tmpNode);
    end;
  end;
end;
                       

function HtmlParse_StockItem(AParseRecord: PParseRecord; ANode: PHtmlDomNode): Boolean;
var
  i, tmpcnt: Integer;
  tmpNode: PHtmlDomNode;
  tmpValue: string;
  tmpOldIsInDiv: integer;
  tmpDealItem: PRT_DealItem;
  tmpStockCode: string;    
  tmpStockName: string;
  tmpIsStock: Boolean;
begin
  result := false;           
  tmpOldIsInDiv := AParseRecord.IsInDiv;
  try
    // div class="quotebody"
    if 0 = AParseRecord.IsInDiv then
    begin
      if SameText('div', string(lowercase(ANode.nodeName))) then
      begin
        if nil <> ANode.attributes then
        begin
          for i := 0 to ANode.attributes.length - 1 do
          begin
            tmpNode := ANode.attributes.item(i);
            if SameText('class', tmpNode.nodeName) then
            begin
              tmpValue := getNodeText(tmpNode);
              if SameText('quotebody', tmpValue) then
              begin
                AParseRecord.IsInDiv := 1;
              end;
            end;
          end;
        end;
      end;
    end;     
    if 1 = AParseRecord.IsInDiv then
    begin
      if SameText('li', string(lowercase(ANode.nodeName))) then
      begin
        AParseRecord.IsInDiv := 2;
      end;
    end;
    if 2 = AParseRecord.IsInDiv then
    begin
      if SameText('a', string(lowercase(ANode.nodeName))) then
      begin
        tmpValue := getAttribValue(ANode, 'href');
        if '' <> tmpValue then
        begin      
          tmpValue := getNodeText(ANode);
          if '' <> tmpValue then
          begin
            i := Pos('(', tmpValue);
            if 0 < i then
            begin
              tmpStockName := Copy(tmpValue, 1, i - 1);
              tmpStockCode := Copy(tmpValue, i + 1, maxint);
              tmpStockCode := Trim(StringReplace(tmpStockCode, ')', '', [rfReplaceAll]));
              if '' <> tmpStockCode then
              begin
                if 6 = Length(tmpStockCode) then
                begin
                  tmpIsStock := ('6' = tmpStockCode[1]) or
                                (('0' = tmpStockCode[1]) and ('0' = tmpStockCode[2])) or
                                ('3' = tmpStockCode[1]);
                  if tmpIsStock then
                  begin
                    if nil <> GlobalBaseStockApp then
                    begin
                      if nil <> GlobalBaseStockApp.StockItemDB then
                      begin
                        tmpDealItem := GlobalBaseStockApp.StockItemDB.FindDealItemByCode(tmpStockCode);
                        if nil = tmpDealItem then
                        begin                     
                          if ('6' = tmpStockCode[1]) then
                          begin
                            tmpDealItem := GlobalBaseStockApp.StockItemDB.AddDealItem(Market_SH, tmpStockCode);
                          end else
                          begin
                            tmpDealItem := GlobalBaseStockApp.StockItemDB.AddDealItem(Market_SZ, tmpStockCode);
                          end;
                          tmpDealItem.Name := tmpStockName;
                        end;
                      end;
                    end;
                    if ('6' = tmpStockCode[1]) then
                    begin
                      AParseRecord.Stocks.Add(Market_SH + tmpStockCode + ',' + tmpStockName);
                    end else
                    begin
                      AParseRecord.Stocks.Add(Market_SZ + tmpStockCode + ',' + tmpStockName);
                    end;
                  end;
                end;
              end;
            end;
          end;
        end;
      end;
    end;
    if nil <> ANode.childNodes then
    begin
      tmpcnt := ANode.childNodes.length;
      for i := 0 to tmpcnt - 1 do
      begin
        tmpNode := ANode.childNodes.item(i);
        if not result then
        begin
          result := HtmlParse_StockItem(AParseRecord, tmpNode);
        end else
        begin
          HtmlParse_StockItem(AParseRecord, tmpNode);
        end;
      end;
    end;
  finally
    AParseRecord.IsInDiv := tmpOldIsInDiv;
  end;
end;

procedure TfmeDealItemViewer.btnRefreshClick(Sender: TObject);   
var
  tmpUrl: string;       
  tmpParseRec: TParseRecord;
  tmpHttpSession: THttpClientSession;
  tmpHttpData: PIOBuffer;
  tmpStr: string;
begin
  inherited;
  FillChar(tmpHttpSession, SizeOf(tmpHttpSession), 0);
  tmpUrl := 'http://quote.eastmoney.com/stocklist.html';
  tmpHttpData := nil;
  tmpHttpData := GetHttpUrlData(tmpUrl, @tmpHttpSession, tmpHttpData);
  if nil <> tmpHttpData then
  begin                
    FillChar(tmpParseRec, SizeOf(tmpParseRec), 0);    
    tmpParseRec.Stocks := TStringList.Create;
    HttpBufferHeader_Parser(tmpHttpData, @tmpParseRec.HeadParse);
    if (199 < tmpParseRec.HeadParse.RetCode) and (300 > tmpParseRec.HeadParse.RetCode)then
    begin                           
      try
        tmpParseRec.HtmlDoc := HtmlParserparseString(WideString(AnsiString(PAnsiChar(@tmpHttpData.Data[tmpParseRec.HeadParse.HeadEndPos + 1]))));
        if tmpParseRec.HtmlDoc <> nil then
        begin
          try
            HtmlParse_StockItem(@tmpParseRec, PHtmlDomNode(tmpParseRec.HtmlDoc));
            tmpParseRec.Stocks.Delimiter := '|';
            tmpStr := tmpParseRec.Stocks.Text;
            if '' <> tmpStr then
            begin
              tmpParseRec.Stocks.Text := StringReplace(tmpStr, #13#10, '|', [rfReplaceAll]);
            end;
            //tmpParseRec.Stocks.SaveToFile('e:\stock.txt');
            TBaseStockApp(App).StockItemDB.Sort;
            BuildDealItemsTree;
          finally
            HtmlDomNodeFree(PHtmlDomNode(tmpParseRec.HtmlDoc));
            tmpParseRec.HtmlDoc := nil;
          end;
        end;
      except
      end;
    end;
  end;
end;

procedure TfmeDealItemViewer.btnClearClick(Sender: TObject);
begin
  inherited;
  fDealItemViewerData.DealItemTree.Clear;    
  TBaseStockApp(App).StockItemDB.Clear;
end;

procedure TfmeDealItemViewer.SaveDealItemDBAsDic(AFileUrl: string);
begin                     
  TBaseStockApp(App).StockItemDB.Sort;
  db_dealItem_Save.SaveDBStockItemToDicFile(App, TBaseStockApp(App).StockItemDB, AFileUrl);
end;

procedure TfmeDealItemViewer.SaveDealItemDBAsIni(AFileUrl: string);
begin
  TBaseStockApp(App).StockItemDB.Sort;
  db_dealItem_Save.SaveDBStockItemToIniFile(App, TBaseStockApp(App).StockItemDB, AFileUrl);
end;
               
procedure TfmeDealItemViewer.btnSaveDicClick(Sender: TObject);
var
  tmpFileUrl: string;
  tmpSaveDlg: TSaveDialog;
begin
  tmpFileUrl := '';                    
  tmpSaveDlg := TSaveDialog.Create(Self);
  try
    tmpSaveDlg.InitialDir := ExtractFilePath(ParamStr(0));
    tmpSaveDlg.DefaultExt := '.dic';
    tmpSaveDlg.Filter := 'dic file|*.dic';
    if not tmpSaveDlg.Execute then
      exit;
    tmpFileUrl := tmpSaveDlg.FileName;
    if '' <> Trim(tmpFileUrl) then
    begin
      SaveDealItemDBAsDic(tmpFileUrl);
    end;
  finally
    tmpSaveDlg.Free;
  end;
end;

procedure TfmeDealItemViewer.btnSaveIniClick(Sender: TObject);
var
  tmpFileUrl: string;
  tmpSaveDlg: TSaveDialog;
begin
  tmpFileUrl := '';                    
  tmpSaveDlg := TSaveDialog.Create(Self);
  try
    tmpSaveDlg.InitialDir := ExtractFilePath(ParamStr(0));
    tmpSaveDlg.DefaultExt := '.ini';
    tmpSaveDlg.Filter := 'ini file|*.ini'; 
    if not tmpSaveDlg.Execute then
      exit;
    tmpFileUrl := tmpSaveDlg.FileName;
    if '' <> Trim(tmpFileUrl) then
    begin
      SaveDealItemDBAsIni(tmpFileUrl);
    end;
  finally
    tmpSaveDlg.Free;
  end;
end;

end.
